/*
 *  Copyright 2003 by Texas Instruments Incorporated.
 *  All rights reserved. Property of Texas Instruments Incorporated.
 *  Restricted rights to use, duplicate or disclose this code are
 *  granted through contract.
 *  
 */
/* "@(#) DDK 1.10.00.23 07-02-03 (ddk-b12)" */
/*
 *  ======== pio.c ========
 */

#include <std.h>

#include <dev.h>
#include <fxn.h>
#include <hwi.h>
#include <pip.h>
#include <que.h>
#include <sys.h>

#include <iom.h>
#include <pio.h>

#include <string.h>     /* for memset() */

static Void rxCallback(Arg arg, IOM_Packet *packet);
static Void txCallback(Arg arg, IOM_Packet *packet);
static Void transfer (PIO_Handle pio, Uns numFrames);

#pragma CODE_SECTION(PIO_init, ".text:init");
#pragma CODE_SECTION(PIO_new, ".text:init");
#pragma CODE_SECTION(PIO_rxPrime, ".text:PIO_rx");
#pragma CODE_SECTION(PIO_rxStart, ".text:PIO_rx");
#pragma CODE_SECTION(PIO_txPrime, ".text:PIO_tx");
#pragma CODE_SECTION(PIO_txStart, ".text:PIO_tx");
#pragma CODE_SECTION(txCallback, ".text:PIO_tx");
#pragma CODE_SECTION(rxCallback, ".text:PIO_rx");

/* 
 * These macros are used to work-around a problem with 'notifyWriter'
 * calls from PIO_txStart() ...
 */
#ifdef _55_
/* PIP_Obj structure is defined differently for 55x */
#define getNotifyWriterFxn(pip)          ((pip)->readerSock.notifyFxn.fxn)
#define setNotifyWriterFxn(pip, notify) \
         ((pip)->readerSock.notifyFxn.fxn = (notify))
#else
#define getNotifyWriterFxn(pip)         ((pip)->notifyWriter.fxn)
#define setNotifyWriterFxn(pip, notify) ((pip)->notifyWriter.fxn = (notify))
#endif

/*
 *  ======== PIO_ATTRS ========
 */
PIO_Attrs PIO_ATTRS = {
    0                           // arg to mdCreateChan
};

/*  ======== PIO_init ========
 *
 */
Void PIO_init(Void)
{
    /*
     *  Module initialization
     */
}

/*
 *  ======== PIO_new ========
 */
Void PIO_new(PIO_Handle pio, PIP_Handle pip, String name, Int mode,
        PIO_Attrs *attrs)
{
    IOM_TiomCallback    cbFxn;
    DEV_Device          *entry;
    Int                 i, status;

    if (attrs == NULL) {
        attrs = &PIO_ATTRS;
    }

    /*
     *  Find device structure in device table for device with name 'name'.
     *  DEV_match() returns the remaining name string for use by the
     *  mini-driver's create() function.
     */
    name = DEV_match(name, &entry);
    if (entry == NULL) {
        SYS_abort("PIO_new: DEV_match() failed\n");
    }

    if (entry->type != DEV_IOMTYPE) {
        SYS_abort("PIO_new: invalid device type\n");
    }

    pio->pip = pip;
    pio->fxns = (IOM_Fxns *)entry->fxns;
    pio->mode = mode;

    pio->submitCount = 0;

    /* Initialize queue structures */
    QUE_new(&pio->freeList);

    /* Initialize the freeList with packets */
    for (i=0; i < PIO_MAXPACKETS; i++)  {
        QUE_put(&pio->freeList, &pio->packet[i]);
    }

    if (mode == IOM_INPUT) {
        cbFxn = (IOM_TiomCallback)rxCallback;
        pio->curdesc = PIP_getWriterCurdesc(pip);
    }
    else {
        cbFxn = (IOM_TiomCallback)txCallback;
        pio->curdesc = PIP_getReaderCurdesc(pip);
    }

    /* open the channel */
    status = pio->fxns->mdCreateChan(&pio->mdChan, entry->devp,
        name, mode, attrs->openArgs, cbFxn, pio);

    if (status != IOM_COMPLETED) {
        SYS_abort("PIO_new: mdCreateChan failed");
    }
}

/*
 *  ======== transfer ========
 *  pio->submitCount
 *  The count of frames submitted to the IO mini-driver is stored
 *  in pio->submitCount. Transfer() looks ahead in the pipe using
 *  the pipe descriptor.  The number of frames taken from the pipe
 *  in this way is kept in pio->submitCount. Therefore the number of
 *  frames available in the pipe is the number of frames indicated by
 *  PIP_getReaderNumFrames() or PIP_getWriterNumFrames()
 *  less the value of pio->submitCount.
 */
static Void transfer (PIO_Handle pio, Uns numFrames)
{
    PIP_Curdesc         *curdesc;
    IOM_Packet          *packet;
    IOM_TiomCallback    cbFxn;
    Int                 status;

    /*
     * check to see if we have an available PIP frame and an IOM_Packet
     * so that we can submit the I/O request to the mini-driver
     */
    if ((numFrames > pio->submitCount) && (pio->submitCount < PIO_MAXPACKETS)) {

        packet = QUE_get(&pio->freeList);

        /* assert(packet != &pio->freeList); */

        curdesc = pio->curdesc;         // pipe descriptor
        pio->curdesc = curdesc->next;   // next pipe descriptor
        pio->submitCount++;             // increment counter

        /* Initialize the packet */
        packet->addr = curdesc->addr;
        packet->size = curdesc->size * sizeof(Int);

        if (pio->mode == IOM_INPUT) {
            packet->cmd = IOM_READ;
            cbFxn = (IOM_TiomCallback)rxCallback;
        }
        else if (pio->mode == IOM_OUTPUT) {
            packet->cmd = IOM_WRITE;
            cbFxn = (IOM_TiomCallback)txCallback;
        }

        status = pio->fxns->mdSubmitChan(pio->mdChan, packet);

        if (status == IOM_COMPLETED) {
            /*
             * Since the mini-driver completed the I/O, the callback will
             * not be called. We need to handle the callback ourselves.
             * This call might cause recursive call back to transfer().
             */
            cbFxn(pio, packet);
        }
        else if (status < 0) {
            /*
             * This should never happen.  IOM mini drivers must be able
             * to queue all submitted IOM_Packets.
             */
            SYS_abort("PIO: mdSubmitChan() failed!");
        }
    }
}

/*
 *  ======== rxCallback ======== 
 */
static Void rxCallback (Arg arg, IOM_Packet *packet)
{
    PIO_Handle pio = (PIO_Handle)(arg);
    PIP_Handle pip = pio->pip;

    /*
     * Must update the pipe before calling submit again.
     * We have used the pointer to the next frame,
     * but PIP_alloc has not been called yet.
     */
    PIP_alloc(pip);

    /* set size and return completed frame - PIP needs size in words! */
    PIP_setWriterSize(pip, packet->size / sizeof(Int));
    PIP_put(pip);

    /* decrement count of submitted frames */
    pio->submitCount--;

    /* Must put the packet back of the freeList */
    QUE_put(&pio->freeList, packet);

    /* start next transfer */
    transfer(pio, PIP_getWriterNumFrames(pio->pip));
}

/*
 *  ======== txCallback ======== 
 */
static Void txCallback (Arg arg, IOM_Packet *packet)
{
    PIO_Handle pio = (PIO_Handle)(arg);
    PIP_Handle pip = pio->pip;
    /*
     * Must update the pipe before calling submit again.
     * We have used the pointer to the next frame,
     * but PIP_get has not been called yet.
     */
    PIP_get(pip);

    /* return completed frame */
    PIP_free(pip);

    /* decrement count of submitted frames */
    pio->submitCount--;

    /* Must put the packet back of the freeList */
    QUE_put(&pio->freeList, packet);

    /* start next transfer */
    transfer(pio, PIP_getReaderNumFrames(pio->pip));
}

/*
 *  ======== PIO_rxPrime ======== 
 *  submit one frame if receive not busy
 */
Void PIO_rxPrime (PIO_Handle pio)
{
    Uns imask;

    imask = HWI_disable();
    transfer(pio, PIP_getWriterNumFrames(pio->pip));
    HWI_restore(imask);
}

/*
 *  ======== PIO_rxStart ======== 
 *  submit frames to receiver
 *
 *  CONSTRAINTS:
 *  -  PIO_rxStart() may only be called in main() before interrupts
 *     are enabled
 */
Void PIO_rxStart (PIO_Handle pio, Uns frameCount)
{
    for ( ; frameCount > 0; frameCount--) {
        transfer(pio, PIP_getWriterNumFrames(pio->pip));
    }
}

/*
 *  ======== PIO_txPrime ======== 
 *  submit one frame if transmit not busy
 */
Void PIO_txPrime (PIO_Handle pio)
{
    Uns imask;

    imask = HWI_disable();
    transfer(pio, PIP_getReaderNumFrames(pio->pip));
    HWI_restore(imask);
}

/*
 *  ======== PIO_txStart ======== 
 *  submit initialized frames to transmitter
 *
 *  CONSTRAINTS:
 *  -  PIO_txStart() may only be called in main() before interrupts
 *     are enabled
 */
Void PIO_txStart (PIO_Handle pio, Uns frameCount, Uns initialValue)
{
    PIP_Handle  pip = pio->pip;
    char        *buf;
    Fxn         notifySave;

    /*
     * Work-around notifyWriter problem at startup.
     * Without this work-around, the PIP_alloc() calls below cause
     * notifyWriter to be called and the application to have incorrect
     * view of the PIP state (application thinks an empty output frame
     * is available when no frame is actually available).
     */
    notifySave = getNotifyWriterFxn(pip);
    setNotifyWriterFxn(pip, (Fxn)FXN_F_nop);

    for ( ; frameCount > 0; frameCount--) {
        if (PIP_getWriterNumFrames(pip) <= 0) break;
        PIP_alloc(pip);
        buf = PIP_getWriterAddr(pip);
        memset(buf, initialValue, PIP_getWriterSize(pip) * sizeof(Int));
        PIP_put(pip);
    }

    /* restore notifyWriter function to its' configured state */
    setNotifyWriterFxn(pip, notifySave);
}

